/*
实现了日志系统的基准测试，主要测试了日志系统的性能，包括日志写入、日志读取、日志删除等操作的耗时。
*/

#include "../include/interface.hpp"
#include <iostream>
#include <chrono>
#include <thread>
#include <vector>

using namespace xzt::log;


Logger::ptr getSyncLogger()
{
    auto builder = std::make_shared<LocalLoggerBulider>();
    builder->bulidLoggerName("sync-logger");
    builder->bulidLoggerType(LoggerType::LOGGER_SYNC);  // 设置日志器为同步日志器, 默认可省略该行
    auto logger = builder->bulid();

    // 动态配置日志器
    ResetFormatter(logger, "%m%n");  // 设置日志格式仅保留消息内容和换行符
    ResetLimitLevel(logger, Level::value::DEBUG);
    std::vector<LogSink::ptr> sinks;
    sinks.emplace_back(std::make_shared<RollingBySizeFileSink>("./logs/sync-log_", 1024 * 1024 * 5));  // 日志落地到滚动文件
    ResetSinks(logger, sinks);

    return logger;
}

Logger::ptr getAsyncLogger()
{
    auto builder = std::make_shared<LocalLoggerBulider>();
    builder->bulidLoggerName("async-logger");
    builder->bulidLoggerType(LoggerType::LOGGER_ASYNC);  // 设置日志器为异步日志器
    builder->bulidEnableAsyncUnSafe();  // 开启异步非安全模式, 仅用于性能测试
    auto logger = builder->bulid();

    // 动态配置日志器
    ResetFormatter(logger, "%m%n");  // 设置日志格式仅保留消息内容和换行符
    ResetLimitLevel(logger, Level::value::DEBUG);
    std::vector<LogSink::ptr> sinks;
    sinks.emplace_back(std::make_shared<RollingBySizeFileSink>("./logs/async-log_", 1024 * 1024 * 5));  // 日志落地到滚动文件
    ResetSinks(logger, sinks);
    
    return logger;
}

void bench_log_system(bool is_sync, const std::string& logger_name, size_t thread_num, 
                    size_t msg_count, size_t msg_size)
{
    // 1. 获取日志器
    // ########################## 同步或异步日志器测试 ###########################
    // auto logger = getSyncLogger();  // 同步日志器测试
    // auto logger = getAsyncLogger();  // 异步日志器测试
    auto logger = is_sync? getSyncLogger(): getAsyncLogger();
    
    if(logger == nullptr)
    {
        std::cerr << "Failed to get default logger" << std::endl;
        return;
    }
    
    // 2. 组织制定长度的日志信息用于测试
    std::string msg(msg_size - 1, 'x');  // 减1是为了留出结尾的'\0'
    // 3. 创建指定数量的线程
    std::vector<std::thread> threads;
    // 存储每个线程的写入日志的耗时
    std::vector<std::chrono::microseconds> durations(thread_num);
    size_t msg_per_thread = msg_count / thread_num;  // 每个线程处理的消息数量
    
    std::cout << "[" << logger_name << "] -> log system with " << thread_num << " threads, " << msg_count 
            << " messages, " << msg_size << " bytes per message" << std::endl;
    for(size_t i = 0; i < thread_num; ++i)
    {
        threads.emplace_back([&durations, &logger, &msg, i, msg_per_thread]() {
            // 线程内部开始计时
            auto start_time = std::chrono::high_resolution_clock::now();
            for(size_t j = 0; j < msg_per_thread; ++j)
            {
                // 4. 写入日志
                logger->info(msg);
            }
            // 线程内部结束计时
            auto end_time = std::chrono::high_resolution_clock::now();
            // 5. 计算写入日志的耗时
            auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
            durations[i] = duration;
            // durations.push_back(duration);  // 多线程并发情况下，可能push_back进去后下标和实际线程不对应，导致结果不准确
            std::cout << "\tThread " << i << " write " << msg_per_thread << " messages, duration: " << duration.count() << " microseconds" << std::endl;
        });
    }
    // 6. 等待所有线程结束
    for(auto& t : threads)
    {
        t.join();
    }

    // 7. 计算写入日志的总耗时，多线程并发情况下，写入日志的总耗时应该等于耗时最长的线程的耗时
    auto max_duration = 0;
    for(auto& duration : durations)
    {
        if(duration.count() > max_duration)
        {
            max_duration = duration.count();
        }
    }
    std::cout << "Total write " << msg_count << " messages, duration: " << max_duration / 1e6 << " seconds" << std::endl;
    // 8. 计算多线程并发情况下，每秒写入日志的数量和字节数
    auto msg_per_sec = msg_count / (max_duration / 1000000.0);
    auto bytes_per_sec = (msg_size * msg_count) / (max_duration / 1000000.0);
    std::cout << "Per second write " << msg_per_sec << " messages and " << bytes_per_sec / 1024.0 << " KB" << std::endl;
}


int main()
{
    bool is_sync = true;
    bench_log_system(is_sync, "sync-singleThread-logger", 1, 1e7, 100);
    std::cout << "--------------------------------------------------------" << std::endl;
    sleep(5);
    bench_log_system(is_sync, "sync-mulitThread-logger", 5, 1e7, 100);
    std::cout << "--------------------------------------------------------" << std::endl;
    sleep(5);
    is_sync = false;
    bench_log_system(is_sync, "async-singleThread-logger", 1, 1e7, 100);
    std::cout << "--------------------------------------------------------" << std::endl;
    sleep(5);
    bench_log_system(is_sync, "async-mulitThread-logger", 5, 1e7, 100);

    return 0;
}

